/*
 * link_list1.c
 *
 *  Created on: 2012-7-30
 *      Author: lin
 */

#include "link_list1.h"
#include "zmalloc/zmalloc.h"

#define List link_list_t
#define Node link_list_node_t
#define Iter link_list_iter_t

static Node* _nodeof(List* list, int32_t idx) {
    int32_t size = list->size;
    assert(idx >= 0);
    assert(idx < size);
    Node* current = NULL;
    int32_t mid = size >> 1;
    if (idx <= mid) {
        current = list->head;
        while (idx--) {
            current = current->next;
        }
    } else {
        current = list->tail;
        idx = size - idx;
        while (--idx) {
            current = current->prev;
        }
    }
    return current;
}

void link_list_clean(List *list) {
    int32_t size = list->size;
    Node *current, *next;

    current = list->head;
    mixed_t_free_fn free_fn = list->free_fn;
    while (size--) {
        next = current->next;
        if (free_fn)
            free_fn(current->val);
        zfree(current);
        current = next;
    }

    list->head = NULL;
    list->tail = NULL;
    list->size = 0;
}

List* link_list_add_head(List *list, mixed_t val) {
    Node *node = zmalloc(sizeof(Node));
    node->val = val;
    if (!list->size) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }
    list->size++;
    return list;
}
List* link_list_add_tail(List *list, mixed_t val) {
    Node *node = zmalloc(sizeof(Node));
    node->val = val;
    if (!list->size) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = list->tail;
        node->next = NULL;
        list->tail->next = node;
        list->tail = node;
    }
    list->size++;
    return list;
}

List* link_list_set(List *list, int32_t idx, mixed_t val) {
    Node node = _nodeof(list, idx);
    mixed_t_free_fn freefn = list->free_fn;
    if (freefn) {
        freefn(node->val);
    }
    node->val = val;
    return list;
}
mixed_t link_list_get(const List *list, int32_t index) {
    Node node = _nodeof(list, idx);
    return node->val;
}
int32_t link_list_index(const List *list, mixed_t match) {
    int32_t size = list->size;
    if (!size) {
        return -1;
    }
    mixed_t_match_fn matchfn = list->match_fn;

    Node* current = list->head;
    mixed_t_match_fn matchfn = list->match_fn;
    int32_t idx = 0;
    if (matchfn) {
        while (idx < size) {
            if (matchfn(current->val, match)) {
                return idx;
            }
            ++idx;
            current = current->next;
        }
    } else {
        while (idx < size) {
            if (current->val.s64 == match.s64) {
                return idx;
            }
            ++idx;
            current = current->next;
        }
    }
    return -1;
}

List* link_list_remove(List *list, int32_t idx) {
    Node* node = _nodeof(list, idx);
    if (!node) {
        return list;
    }

    if (node->prev) {
        node->prev->next = node->next;
    } else {
        list->head = node->next;
    }
    if (node->next) {
        node->next->prev = node->prev;
    } else {
        list->tail = node->prev;
    }
    mixed_t_free_fn freefn = list->free_fn;
    if (freefn) {
        freefn(node->val);
    }
    zfree(node);
    list->size--;
    return list;
}

Iter *link_list_iter_new(const List *list, int32_t reverse) {
    Iter *iter = zmalloc(sizeof(Iter));
    iter->next = reverse ? list->tail : list->head;
    iter->reverse = reverse;
    return iter;
}
void link_list_rewind(const List *list, Iter *iter, int32_t reverse) {
    iter->next = reverse ? list->tail : list->head;
    iter->reverse = reverse;
}
mixed_t link_list_next(Iter *iter, int* hasnext) {
    Node *current = iter->next;
    if (current) {
        iter->next = iter->reverse ? current->prev : current->next;
        *hasnext = 1;
        return current->val;
    }
    *hasnext = 0;
    mixed_t val;
    return val;
}

List *link_list_dup(const List *orig) {
    List *copy = link_list_new();

    mixed_t_dup_fn dupfn = copy->dup_fn = orig->dup_fn;
    copy->free_fn = orig->free_fn;
    copy->match_fn = orig->match_fn;

    Node* current = orig->head;
    while (current) {
        mixed_t val;
        if (dupfn) {
            if (dupfn(current->val, &val)) {
                link_list_free(copy);
                return NULL;
            }
        } else {
            val = current->val;
        }
        link_list_add_tail(copy, val);
        current = current->next;
    }

    return copy;
}
List link_list_rotate(List *list) {
    if (list->size <= 1)
        return list;

    Node *tail = list->tail;
    /* Detatch current tail */
    list->tail = tail->prev;
    list->tail->next = NULL;
    /* Move it as head */
    list->head->prev = tail;
    tail->prev = NULL;
    tail->next = list->head;
    list->head = tail;
    return list;
}

int32_t link_list_to_array(const List *list, mixed_t * array, int32_t sizeto) {
    int32_t size = list->size;
    if (sizeto > size) {
        sizeto = size;
    }
    Node *node = list->head;
    for (int32_t i = 0; i < sizeto; i++) {
        array[i] = node->val;
        node = node->next;
    }
    return size_to;
}

#undef List
#undef Node
#undef Iter
